#include <i386/stdlib.h>
#include <i386/string.h>
#include <i386/limits.h>
#include <i386/mem.h>
#include <stdarg.h>
#include <ctype.h>
#include "sprintf.h"

int vsprintf(char *buf, char *fmt, va_list parms) {
	int scanned = 0, w = 0, prec = 0, l = 0, size = 0;
	long long n1 = 0;
	unsigned long long n2 = 0;
	unsigned parsing = 0, flag = 0;
	char *base = buf;
	char *sp = NULL;
	void *fp = NULL;
	
	while (*fmt != 0) {
		if (*fmt != '%' && !parsing) {
			*buf++ = *fmt++;
			scanned++;
		} else {	    
			if (*fmt == '%') {
				fmt++;
				parsing = 1;
				w = 10;
				prec = 4;
				size = STD_SIZE;
				flag = 0;
			}
			
			// handle token
			switch(*fmt) {
				case '%':
					*buf++ = '%';
					scanned++;
					parsing = 0;
					break;
				case 'c':
					*buf++ = va_arg(parms, char);
					scanned++;
					parsing = 0;
					break;
				case 'i':
			 	case 'd':
			 		switch (size) {
						case STD_SIZE:
							n1 = va_arg(parms, int);
							break;
						case LONG_SIZE:
							n1 = va_arg(parms, long long);
							break;
						case SHORT_SIZE:
							n1 = va_arg(parms, short int);
							break;
					}
					
					l = dcvt(n1, buf, 10, w, flag);
					scanned += l;
					buf += l;
					parsing = 0;
					break;
				case 'u':
					switch (size) {
						case STD_SIZE:
							n2 = va_arg(parms, unsigned);
							break;
						case LONG_SIZE:
							n2 = va_arg(parms, unsigned long long);
							break;
						case SHORT_SIZE:
							n2 = va_arg(parms, unsigned short);
							break;
					}
					
					l = ucvt(n2, buf, 10, w, flag);
					scanned += l;
					buf += l;
					parsing = 0;
					break;
				case 'x':
					switch (size) {
						case STD_SIZE:
							n2 = va_arg(parms, unsigned);
							break;
						case LONG_SIZE:
							n2 = va_arg(parms, unsigned long long);
							break;
						case SHORT_SIZE:
							n2 = va_arg(parms, unsigned short);
							break;
					}
					
					l = ucvt(n2, buf, 16, w, flag);
					scanned += l;
					buf += l;
					parsing = 0;
					break;
				case 'p':
					fp = va_arg(parms, void *);
					n2 = (unsigned long)(fp);
					
					l = ucvt(n2, buf, 16, w, ZERO_PAD);
					
					scanned += l;
					buf += l;
					parsing = 0;
					break;
				case 's':
					sp = va_arg(parms, char *);
					
					while (*sp != 0 && !((flag & RESPECT_WIDTH) && prec-- == 0)) {
						*buf++ = *sp++;
						l++;
					}
					
					scanned += l;
					parsing = 0;
					break;
				case 'l':
					size = LONG_SIZE;
					break;
				case 'n':
				case 'h':
					size = SHORT_SIZE;
					break;
				case '+':
					flag |= ADD_PLUS;
					break;
				case '-':
					flag |= LEFT_PAD;
					break;
				case '.':
					parsing = 2;
					flag |= RESPECT_WIDTH;
					break;
				case '1':
				case '2':
				case '3':
				case '4':
				case '5':
				case '6':
				case '7':
				case '8':
				case '9':
				case '0':
					if (parsing == 1) {
						w = strtou(fmt, 10, &base);
						
						if (*fmt != '0' || flag & LEFT_PAD) {
							flag |= SPACE_PAD ;
						} else {
							flag |= ZERO_PAD ;
						}
						fmt = base - 1;
					} else if (parsing == 2) {
						prec = strtou(fmt, 10, &base);
						fmt = base - 1;
						parsing = 1;
					}
					break;
				default:
					parsing = 0;
					break;
			}
			fmt++;
		}
	}
	
	*buf = 0;
	return scanned;
}

int sprintf(char *buf, char *fmt, ...) {
	va_list parms;
	int result;
	
	va_start(parms, fmt);
	result = vsprintf(buf, fmt, parms);
	va_end(parms);
	
	return result;
}
